/*
 * This file is part of aion-engine <aion-engine.com>
 *
 * aion-engine is private software: you can redistribute it and or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Private Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aion-engine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser Public License
 * along with aion-engine.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aionengine.gameserver.ai2.handler;

import com.aionemu.commons.utils.Rnd;
import com.aionengine.gameserver.ai2.NpcAI2;
import com.aionengine.gameserver.dataholders.DataManager;
import com.aionengine.gameserver.model.gameobjects.Creature;
import com.aionengine.gameserver.model.gameobjects.Npc;
import com.aionengine.gameserver.model.gameobjects.player.Player;
import com.aionengine.gameserver.model.templates.npcshout.NpcShout;
import com.aionengine.gameserver.model.templates.npcshout.ShoutEventType;
import com.aionengine.gameserver.model.templates.npcshout.ShoutType;
import com.aionengine.gameserver.model.templates.walker.WalkerTemplate;
import com.aionengine.gameserver.services.NpcShoutsService;
import com.aionengine.gameserver.utils.ThreadPoolManager;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author Rolandas
 */
public final class ShoutEventHandler {

    public static void onSee(NpcAI2 npcAI, Creature target) {
        Npc npc = npcAI.getOwner();
        if (DataManager.NPC_SHOUT_DATA.hasAnyShout(npc.getPosition().getMapId(), npc.getNpcId(), ShoutEventType.SEE)) {
            List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(npc.getPosition().getMapId(), npc.getNpcId(),
                    ShoutEventType.SEE, null, 0);
            NpcShoutsService.getInstance().shout(npc, target, shouts, 0, false);
            shouts.clear();
        }
    }

    public static void onBeforeDespawn(NpcAI2 npcAI) {
        Npc npc = npcAI.getOwner();
        if (DataManager.NPC_SHOUT_DATA.hasAnyShout(npc.getPosition().getMapId(), npc.getNpcId(),
                ShoutEventType.BEFORE_DESPAWN)) {
            List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(npc.getPosition().getMapId(), npc.getNpcId(),
                    ShoutEventType.BEFORE_DESPAWN, null, 0);
            NpcShoutsService.getInstance().shout(npc, null, shouts, 0, false);
            shouts.clear();
        }
    }

    public static void onReachedWalkPoint(NpcAI2 npcAI) {
        Npc npc = npcAI.getOwner();
        WalkerTemplate tp = DataManager.WALKER_DATA.getWalkerTemplate(npc.getSpawn().getWalkerId());
        int stepCount = tp.getRouteSteps().size();
        ShoutEventType shoutType = npc.getMoveController().isChangingDirection() ? ShoutEventType.WALK_DIRECTION
                : ShoutEventType.WALK_WAYPOINT;
        if (DataManager.NPC_SHOUT_DATA.hasAnyShout(npc.getPosition().getMapId(), npc.getNpcId(), shoutType)) {
            if (Rnd.get(stepCount) < 2) {
                List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(npc.getPosition().getMapId(), npc.getNpcId(),
                        shoutType, null, 0);
                if (npc.getTarget() instanceof Creature)
                    NpcShoutsService.getInstance().shout(npc, (Creature) npc.getTarget(), shouts, 0, false);
                else
                    NpcShoutsService.getInstance().shout(npc, null, shouts, 0, false);
                shouts.clear();
            }
        }
    }

    public static void onSwitchedTarget(NpcAI2 npcAI, Creature creature) {
        Npc npc = npcAI.getOwner();
        if (DataManager.NPC_SHOUT_DATA.hasAnyShout(npc.getPosition().getMapId(), npc.getNpcId(),
                ShoutEventType.SWITCH_TARGET)) {
            List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(npc.getPosition().getMapId(), npc.getNpcId(),
                    ShoutEventType.SWITCH_TARGET, null, 0);
            NpcShoutsService.getInstance().shout(npc, creature, shouts, 0, false);
            shouts.clear();
        }
    }

    public static void onDied(NpcAI2 npcAI) {
        Npc owner = npcAI.getOwner();
        if (DataManager.NPC_SHOUT_DATA.hasAnyShout(owner.getPosition().getMapId(), owner.getNpcId(), ShoutEventType.DIED)) {
            List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(owner.getPosition().getMapId(), owner.getNpcId(),
                    ShoutEventType.DIED, null, 0);
            if (shouts.size() > 0)
                NpcShoutsService.getInstance().shout(owner, (Creature) owner.getTarget(), shouts, 0, false);
            shouts.clear();
        }
    }

    // TODO: Figure out what the difference between ATTACK_BEGIN and HELP; HELPCALL should make NPC run

    /**
     * Called on Aggro when NPC is ready to attack
     */
    public static void onAttackBegin(NpcAI2 npcAI, Creature creature) {
        Npc npc = npcAI.getOwner();
        if (DataManager.NPC_SHOUT_DATA.hasAnyShout(npc.getPosition().getMapId(), npc.getNpcId(),
                ShoutEventType.ATTACK_BEGIN)) {
            List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(npc.getPosition().getMapId(), npc.getNpcId(),
                    ShoutEventType.ATTACK_BEGIN, null, 0);
            NpcShoutsService.getInstance().shout(npc, creature, shouts, 0, false);
            shouts.clear();
            return;
        }
    }

    /**
     * Handle NPC attacked event (when damage was received or not)
     */
    public static void onHelp(NpcAI2 npcAI, Creature creature) {
        // TODO: [RR] change AI or randomise behaviour for "cowards" and "fanatics" ???
        Npc npc = npcAI.getOwner();
        if (npc.getAttackedCount() == 0) {
            if (DataManager.NPC_SHOUT_DATA.hasAnyShout(npc.getPosition().getMapId(), npc.getNpcId(), ShoutEventType.ATTACKED)) {
                List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(npc.getPosition().getMapId(), npc.getNpcId(),
                        ShoutEventType.ATTACKED, null, 0);
                NpcShoutsService.getInstance().shout(npc, creature, shouts, 0, false);
                shouts.clear();
                return;
            }
            if (DataManager.NPC_SHOUT_DATA.hasAnyShout(npc.getPosition().getMapId(), npc.getNpcId(), ShoutEventType.HELPCALL)) {
                List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(npc.getPosition().getMapId(), npc.getNpcId(),
                        ShoutEventType.HELPCALL, null, 0);
                NpcShoutsService.getInstance().shout(npc, creature, shouts, 0, false);
                shouts.clear();
            }
        }
    }

    /**
     * Handles attacks from NPC to NPC.
     * <br>
     * <b><font color='red'>IMPORTANT!!! </font>All such shouts must be of type SAY and poll_delay specified.</b>
     */
    public static void onEnemyAttack(NpcAI2 npcAI, Creature target) {
        final Npc npc = npcAI.getOwner();
        if (!DataManager.NPC_SHOUT_DATA.hasAnyShout(npc.getPosition().getMapId(), npc.getNpcId(), ShoutEventType.ATTACKED))
            return;

        List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(npc.getPosition().getMapId(), npc.getNpcId(),
                ShoutEventType.ATTACKED, null, 0);

        List<NpcShout> finalShouts = new ArrayList<NpcShout>();
        for (NpcShout s : shouts) {
            if (s.getShoutType() == ShoutType.SAY)
                finalShouts.add(s);
        }

        if (finalShouts.size() == 0)
            return;

        int randomShout = Rnd.get(finalShouts.size());
        final NpcShout shout = finalShouts.get(randomShout);
        finalShouts.clear();
        shouts.clear();

        if (!npc.mayShout(shout.getPollDelay() / 1000))
            return;

        ThreadPoolManager.getInstance().schedule(new Runnable() {

            @Override
            public void run() {
                Iterator<Player> iter = npc.getKnownList().getKnownPlayers().values().iterator();
                while (iter.hasNext()) {
                    Player kObj = iter.next();
                    if (kObj.getLifeStats().isAlreadyDead())
                        return;
                    NpcShoutsService.getInstance().shout(npc, kObj, shout, shout.getPollDelay() / 1000);
                }
            }
        }, 0);
    }

    public static void onCast(NpcAI2 npcAI, Creature creature) {
        handleNumericEvent(npcAI, creature, ShoutEventType.CAST_K);
    }

    /**
     * Handle target attacked events
     */
    public static void onAttack(NpcAI2 npcAI, Creature creature) {
        handleNumericEvent(npcAI, creature, ShoutEventType.ATTACK_K);
    }

    private static void handleNumericEvent(NpcAI2 npcAI, Creature creature, ShoutEventType eventType) {
        Npc owner = npcAI.getOwner();
        List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(owner.getPosition().getMapId(), owner.getNpcId(),
                eventType, null, 0);
        if (shouts == null)
            return;

        List<NpcShout> validShouts = new ArrayList<NpcShout>();
        List<NpcShout> nonNumberedShouts = new ArrayList<NpcShout>();
        for (NpcShout shout : shouts) {
            if (shout.getSkillNo() == 0)
                nonNumberedShouts.add(shout);
            else if (shout.getSkillNo() == owner.getSkillNumber())
                validShouts.add(shout);
        }

        if (validShouts.size() == 0) {
            validShouts.clear();
            validShouts = nonNumberedShouts;
        } else {
            nonNumberedShouts.clear();
        }

        if (validShouts.size() > 0) {
            NpcShoutsService.getInstance().shout(owner, creature, validShouts, 0, false);
        }

        validShouts.clear();
        shouts.clear();
    }

    public static void onAttackEnd(NpcAI2 npcAI) {
        Npc npc = npcAI.getOwner();
        if (DataManager.NPC_SHOUT_DATA.hasAnyShout(npc.getPosition().getMapId(), npc.getNpcId(), ShoutEventType.ATTACK_END)) {
            List<NpcShout> shouts = DataManager.NPC_SHOUT_DATA.getNpcShouts(npc.getPosition().getMapId(), npc.getNpcId(),
                    ShoutEventType.ATTACK_END, null, 0);
            NpcShoutsService.getInstance().shout(npc, null, shouts, 0, false);
            shouts.clear();
        }
    }

}
